/* server被webpack启动时使用的模块，因此使用commonjs规范 */

const Mock = require('mockjs');
const chalk = require('chalk');
const chokidar = require('chokidar');
const bodyParser = require('body-parser');

const path = require('path');
const mockDir = path.join(process.cwd(), 'mock'); //mock目录

const { injectServerMocks } = require('./utils');
const { baseURL } = require('../src/config/settings');

/* 把mock转换为可被express使用的路由参数 */
const transRoute = (url, type = 'get', respond) => {
  return {
    url: new RegExp(baseURL + url),
    type,
    response(req, res) {
      res.status(200);
      console.log(chalk.green(`> 请求地址：[${req.method}] ${req.path}`));
      if (JSON.stringify(req.body) !== '{}') {
        console.log(chalk.green(`> 请求参数：${JSON.stringify(req.body)}`));
      }
      return res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond));
    },
  };
};

/* 将mocks注册为express的server服务 */
const registerServers = app => {
  const mocks = injectServerMocks();
  const routers = mocks.map(({ url, type, response }) => transRoute(url, type, response));
  routers.forEach(({ url, type, response }) => {
    app[type](url, response); // 真正的server
  });
  const mockLastIndex = app._router.stack.length;
  const mockRoutesLength = routers.length;
  const mockStartIndex = mockLastIndex - mockRoutesLength;
  return {
    mockStartIndex,
    mockRoutesLength,
  };
};

const r = require;

/* 当任何mock文件变动时热更新所有mocks和对应servers */
module.exports = app => {
  // 启用babel转换，后续代码运行时require/import进来的模块将被babel转换
  // 热更新是异步工作的，只有当有文件变化时才会执行；
  require('@babel/register');
  app.use(bodyParser.json());
  app.use(
    bodyParser.urlencoded({
      extended: true,
    })
  );
  let { mockStartIndex, mockRoutesLength } = registerServers(app);
  chokidar
    .watch(mockDir, {
      // 忽略监控mock-server.js文件变化
      ignored: /mock-server/,
      ignoreInitial: true,
    })
    .on('all', event => {
      if (event === 'change' || event === 'add') {
        try {
          app._router.stack.splice(mockStartIndex, mockRoutesLength);
          Object.keys(require.cache).forEach(item => {
            if (item.includes(mockDir)) {
              delete require.cache[require.resolve(item)];
            }
          });
          const register = registerServers(app);
          mockStartIndex = register.mockStartIndex;
          mockRoutesLength = register.mockRoutesLength;
        } catch (error) {
          console.log(chalk.red(error));
        }
      }
    });
};
